#!/bin/sh
IFS=$' \t\n' #IFS = Internal Field Separator, set to space, tab, and newline (bash defaults)
LANG= #Keep the locale out of the way

DIGIT_PATTERN=[[:digit:]][[:digit:]][.][[:digit:]][[:digit:]][.][[:digit:]][[:digit:]]
PATTERN1="#ident[[:space:]]\+\"Honeywell $DIGIT_PATTERN\""	#ident macros are assumed to allow quotes or angle brackets, like #includes
PATTERN2="#ident[[:space:]]\+<Honeywell $DIGIT_PATTERN>"
PATTERN3="# [^[:space:]]\+ Firmware v. ${DIGIT_PATTERN}[.][[:digit:]][[:digit:]]"
BACKUP_MESSAGE=\(Backup\ copy\ saved\ in
MODE_SWITCH=; FILENAME=; VER_ARG=; QUERY=
if [ ! "`echo $@ |sed s/[[:space:]]*//g`" ] ; then MODE_SWITCH='-h'; fi
for arg in "$@"; do
  if [ ! "$arg" ] ; then : #ignore empty arguments
  elif [ '-' = "`echo "$arg" |head -c 1`" ] ; then
    if [ "$arg" = '-nb' -o $arg = '--no-backup' ] ; then
      BACKUP_MESSAGE=
    elif [ "$MODE_SWITCH" ] ; then
      echo Conflicting switches: \""$MODE_SWITCH"\" and \""$arg"\". >&2; exit 1
    else MODE_SWITCH="$arg"
    fi
  elif [ \( ! "$VER_ARG" \) -a \( '-s' = "$MODE_SWITCH" -o '--set' = "$MODE_SWITCH" \) ] ; then
    VER_ARG="$arg"
  elif [ "$FILENAME" ] ; then
    echo Multiple filenames provided:  \""$FILENAME"\" and \""$arg"\". >&2; exit 1
  else FILENAME="$arg"
  fi
done
case "$MODE_SWITCH" in
'-h'|'--help')
  echo
  echo '  -h, --help           Print this message.'
  echo '  -q, --query   FILE   Print the version number of FILE in ##.##.## form.'
  echo '  <no switch>   FILE   Increment version macro(s) in FILE.'
  echo '  -m            FILE'
  echo '      --minor   FILE   Increment minor version number (middle number);'
  echo '                        clear build number (final number) to 00.'
  echo '  -M            FILE'
  echo '      --major   FILE   Increment major version number (first number);'
  echo '                        clear build and minor numbers (the others) to 00.00.'
  echo '  -s        VER FILE'
  echo '      --set VER FILE   Set the version of FILE to VER, given as ##.##.## .'
  echo '  -nb,--no-backup      (With usages that modify version) Do not backup FILE'
  echo '                        (normally appended to FILE.bak) before writing to it.'
  echo '  -r DIR, -R DIR       Disabled:  Recursively find files in DIR with version'
  echo '                        information, and increment their versions.  Too little'
  echo '                        benefit to justify the risk of incrementing files that'
  echo '                        should not be incremented.'
  echo 'Note that incrementing a 99 acts the same as incrementing the next-most-significant version number.  E.g., 98.99.99 increments to 99.00.00, regardless of incrementation type.'
  echo 'Each time version macros in FILE are incremented, its pre-increment contents are appended to FILE.bak in case I screwed something up.  If FILE is large, you may wish to remove FILE.bak often.  Or make a habit of using the no-backup switch.'
  echo
  exit
;;
'-q'|'--query')
  QUERY=yes
  BACKUP_MESSAGE=
;;
'-m'|'--minor')
  MINOR=yes
;;
'-M'|'--major')
  MAJOR=yes
;;
'-s'|'--set')
  if [ 1 -ne `echo "$VER_ARG" |grep -c ^"$DIGIT_PATTERN"$` ] ; then
    echo Invalid version format.  Use '##.##.##' . >&2; exit 1
  fi
;;
'-r'|'-R')
  echo Recursive version incrementation is no longer supported:  insufficient benefit to justify the risk of bumping independently versioned code. >&2; exit 1
#  BASE_PATH=$2
#  echo Searching for files with version macros.  This may take a few seconds.
#  for FILE in `grep -RIl "$PATTERN1" "$BASE_PATH" |grep -v [.]bak$ |grep -v ~$; grep -RIl "$PATTERN2" "$BASE_PATH" |grep -v [.]bak$ |grep -v ~$`; do
#    version++ "$FILE"
#  done
;;
'');;
*)
  echo Invalid option \'"$MODE_SWITCH"\'. >&2; exit 1
esac

if [ ! "$FILENAME" ] ; then
  if [ ! "$QUERY" ] ; then echo No file provided. >&2; exit 1; fi
  FILE=`cat /dev/stdin`
else
  if [ ! -f "$FILENAME" ] ; then
    echo File "$FILENAME" is not a regular file. >&2; exit 1
  fi
  if [ ! -r "$FILENAME" ] ; then
    echo File "$FILENAME" does not exist or is not readable. >&2; exit 1
  fi
  if [ \( ! -w "$FILENAME" \) -a \( ! "$QUERY" \) ] ; then
    chmod a+w "$FILENAME"
    if [ $? -ne 0 ] ; then
      echo File "$FILENAME" not writable, and chmod failed. >&2; exit 1
    fi
  fi
  FILE=`cat "$FILENAME"`
fi
if [ "$BACKUP_MESSAGE" ] ; then
  EXT=bak
  BACKUP="$FILENAME"."$EXT"
  BACKUP_MESSAGE="$BACKUP_MESSAGE $BACKUP)."
  touch "$BACKUP"
  if [ ! -w "$BACKUP" ] ; then
    chmod a+w "$BACKUP"
    if [ $? -ne 0 ] ; then
      echo Backup file \""$BACKUP"\" not writable, and chmod failed. >&2; exit 1
    fi
  fi
  cat "$FILENAME" >> "$BACKUP"
  if [ $? -ne 0 ] ; then
    echo Backup failed. >&2; exit 1
  fi
fi

COUNT1=`echo "$FILE" |grep -c "$PATTERN1"`	#Make sure exactly one match exists and note which pattern is matched.
COUNT2=`echo "$FILE" |grep -c "$PATTERN2"`	#Make sure exactly one match exists and note which pattern is matched.
COUNT3=`echo "$FILE" |grep -c "$PATTERN3"`	#Make sure exactly one match exists and note which pattern is matched.
if [ "$COUNT1" -eq 1 ] ; then
  WHICH_PATTERN=1; PATTERN="$PATTERN1"
elif [ "$COUNT2" -eq 1 ] ; then
  WHICH_PATTERN=2; PATTERN="$PATTERN2"
elif [ "$COUNT3" -eq 1 ] ; then
  WHICH_PATTERN=3; PATTERN="$PATTERN3"
fi
let COUNT=COUNT1+COUNT2+COUNT3
if [ "$COUNT" -gt 1 ] ; then
  echo File \""$FILENAME"\" has "$COUNT" '#ident' macros conforming to the standard form.  \(I look within comments, too\). >&2; exit 1
else
  PATTERNB[2]=\#define\ \\\+VERSION_MAJOR\ \\\+[[:digit:]]\\\{1,2\\\}\\\($\\\|[^[:digit:]]\\\)
  PATTERNB[1]=\#define\ \\\+VERSION_MINOR\ \\\+[[:digit:]]\\\{1,2\\\}\\\($\\\|[^[:digit:]]\\\)
  PATTERNB[0]=\#define\ \\\+VERSION_BUILD\ \\\+[[:digit:]]\\\{1,2\\\}\\\($\\\|[^[:digit:]]\\\)
  for i in 0 1 2 ; do
    COUNTI=`echo "$FILE" |grep -c "${PATTERNB[$i]}"`
    if [ "$COUNTI" -eq 0 ] ; then
      if [ "$COUNT" -eq 0 ] ; then
	echo File \"$FILENAME\" does not have the expected version macros. >&2; exit 1
      fi
      i=; break
    fi
    if [ "$COUNTI" -gt 1 ] ; then
      echo File \""$FILENAME"\" has "$COUNTI" valid \"`echo ${PATTERNB[$i]} |sed s/\\\\\\\+//g |head -c 21`\" macros. \(I look within comments, too\). >&2; exit 1
    fi
    if [ 0 -ne `echo "$FILE" |grep -c "\`echo ${PATTERNB[$i]} |head -c 37\`"\\\\\\{3,\\\\\\}` ] ; then
      echo Found macro with excess digits.  \(I look within comments, too\). >&2; exit 1
    fi
    MATCH[i]=`echo "$FILE" |grep -o "${PATTERNB[$i]}" |sed s/[^[:digit:]]$//`
    VALUE[i]=`echo "${MATCH[$i]}" |awk --source='{print $3}' |sed s/^0//`
    if [ ! "${VALUE[$i]}" ] ; then VALUE[i]=0 ; fi
    let digit[$(( 2*i ))]=${VALUE[$i]}%10
    let digit[$(( 2*i+1 ))]=${VALUE[$i]}/10
  done
  if [ "$i" ] ; then
    VERSION=${digit[5]}${digit[4]}.${digit[3]}${digit[2]}.${digit[1]}${digit[0]}
    if [ "$QUERY" ] ; then echo "$VERSION"; exit 0; fi
    OUTPUT="$FILENAME:  Changing current version $VERSION to"
    if [ "$VER_ARG" ] ; then
      let VERSION=`echo -n 1"$VER_ARG" |awk -F . --source='{print $1$2$3}'`-1000000
    elif [ "$MAJOR" ] ; then
      let VERSION=10000*${VALUE[2]}+10000
    elif [ "$MINOR" ] ; then
      let VERSION=100*${VALUE[1]}+10000*${VALUE[2]}+100
    else
      let VERSION=${VALUE[0]}+100*${VALUE[1]}+10000*${VALUE[2]}+1
    fi
    if [ "$VERSION" -gt 999999 ] ; then echo New version exceeds '##.##.##' format. >&2; exit 1; fi
    mutableVERSION="$VERSION"						#From now on, $VERSION will reliably be the new version, without leading zeros or decimal points.
    for digitIndex in 0 1 2 3 4 5; do					#Find all six digits of the new version number.
      let digit[$digitIndex]=$mutableVERSION%10
      let mutableVERSION/=10
    done
    mutableVERSION=${digit[5]}${digit[4]}.${digit[3]}${digit[2]}.${digit[1]}${digit[0]}	#back to ##.##.## format, with new version number now.
    OUTPUT="$OUTPUT $mutableVERSION $BACKUP_MESSAGE"
    for i in 0 1 2 ; do
      NEWMACRO[i]=`echo "${PATTERNB[$i]}" |sed s/\\\\\\\+//g |sed s/\\\[\\\[.*$/"$(( ${digit[$(( 2*i ))]}+10*${digit[(( 2*i+1 ))]} ))"/`
    done
    if [ "$BACKUP_MESSAGE" ] ; then
      if [ `tail --bytes=\`ls -l "$FILENAME" |awk --source='{print $5}'\` "$FILENAME"."$EXT" |diff -q - "$FILENAME" 2>&1` ] ; then
        echo Unexpected error:  Input file does not match end of backup file. >&2; exit 1
      fi
    fi
    echo "$OUTPUT"; FILE="`echo "$FILE" |sed -e s/"${MATCH[0]}"/"${NEWMACRO[0]}"/ -e s/"${MATCH[1]}"/"${NEWMACRO[1]}"/ -e s/"${MATCH[2]}"/"${NEWMACRO[2]}"/`"
    if [ "$COUNT" -eq 0 ] ; then
      echo "$FILE" > "$FILENAME"
      exit
    fi
  fi
fi

MATCH=`echo "$FILE" |grep -o "$PATTERN"`
VERSION=`echo "$MATCH" |grep -o  "$DIGIT_PATTERN"`			#VERSION is now in ##.##.## format
OUTPUT="$FILENAME:  Changing current version $VERSION to"
if [ "$QUERY" ] ; then echo "$VERSION"; exit 0; fi
let VERSION=`echo -n 1"$VERSION" |awk -F . --source='{print $1$2$3}'`-1000000	#Remove decimal points and any leading zeros).
if [ "$VER_ARG" ] ; then
  let VERSION=`echo -n 1"$VER_ARG" |awk -F . --source='{print $1$2$3}'`-1000000
elif [ "$MAJOR" ] ; then
  let VERSION/=10000; let VERSION++; let VERSION*=10000
elif [ "$MINOR" ] ; then
  let VERSION/=100; let VERSION++; let VERSION*=100
else
  let VERSION++
fi
if [ "$VERSION" -gt 999999 ] ; then echo New version exceeds '##.##.##' format. >&2; exit 1; fi

mutableVERSION="$VERSION"						#From now on, $VERSION will reliably be the new version, without leading zeros or decimal points.
for digitIndex in 0 1 2 3 4 5; do					#Find all six digits of the new version number.
  let digit[$digitIndex]=$mutableVERSION%10
  let mutableVERSION/=10
done
mutableVERSION=${digit[5]}${digit[4]}.${digit[3]}${digit[2]}.${digit[1]}${digit[0]}	#back to ##.##.## format, with new version number now.
OUTPUT="$OUTPUT $mutableVERSION $BACKUP_MESSAGE"
if [ "$WHICH_PATTERN" -eq 1 ] ; then
  NEWMACRO="#ident \"Honeywell ${mutableVERSION}\""
elif [ "$WHICH_PATTERN" -eq 2 ] ; then
  NEWMACRO="#ident <Honeywell ${mutableVERSION}>"
else
  PRODUCT="`echo "$MATCH" |cut -d " " -f 2`"
  NEWMACRO="# ${PRODUCT} Firmware v. ${mutableVERSION}.00"
fi
FILESIZE=`ls -l "$FILENAME" |awk --source='{print $5}'`
if [ "$BACKUP_MESSAGE" ] ; then
  if [ `tail --bytes="$FILESIZE" "$FILENAME"."$EXT" |diff -q - "$FILENAME" 2>&1` ] ; then
    echo Unexpected error:  Input file does not match end of backup file. >&2; exit 1
  fi
fi
echo "$OUTPUT"; echo "$FILE" |sed s/"$PATTERN"/"$NEWMACRO"/ > "$FILENAME"
